﻿// Copyright richyp 2009
using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System.Text;
using Richyp.Bodger.Parser;
using System.Windows.Forms;
namespace Richyp.Bodger {
    public class CodexParser : BaseParser {
        #region BaseParser Members

        public void Initialize() {
            
        }

        public ParsedDocument Parse(string file) {
            return this.Parse(file, false);
        }

        public ParsedDocument Parse(string file,bool headerOnly) {
            CodexFile codexFile = new CodexFile();
            //codexFile.Path = Application.StartupPath + "/" + file;
            codexFile.Path = file;
            //file = codexFile.Path + "/index.xml";
            //file += "/index.xml";
            if (codexFile.Path.IndexOf(":") == -1) {
                codexFile.Path = Application.StartupPath + "/" + codexFile.Path;
            }
            if (!File.Exists(codexFile.Path + "/index.xml")) {
                throw new FileNotFoundException(codexFile.Path + "/index.xml");
            }
            XmlDocument document = new XmlDocument();
            try {
                document.Load(codexFile.Path + "/index.xml");
            } catch {
                throw new XmlException("Not a valid codex file: " + file);
            }
            
            Console.WriteLine("DocumentParser: Parsing File " + file);
            codexFile.RawData = document;
            try {
                codexFile = this.ParseHeader(codexFile);
                codexFile = this.ParseSpecialRules(codexFile);
                if (!headerOnly) {
                    codexFile = this.ParseWargear(codexFile, null);
                    Wargear dummy = new Wargear();
                    dummy.ID = -1;
                    dummy.Name = "None";
                    dummy.Description = "";
                    dummy.Type = "Other";
                    codexFile.Wargear.Add(dummy);
                    codexFile = this.ParseBaseUnits(codexFile);
                    codexFile = this.DelayedParse(codexFile);
                    codexFile = this.ParseTransports(codexFile,null);
                    codexFile = this.ParseArmoury(codexFile, null); 
                    codexFile = this.ParseUnitEntries(codexFile, null);
                }
            } catch {
                throw;
            }
            // Do final parse for dependant objects
            Console.WriteLine("DocumentParser: Parsing Finished");
            return codexFile;
        }

        private CodexFile ParseHeader(CodexFile codexFile) {
            XmlNodeList headerNodes = codexFile.RawData.GetElementsByTagName("header");
            if (headerNodes.Count < 1) {
                throw new XmlException("Header entries missing from codex");
            }
            foreach (XmlNode child in headerNodes[0].ChildNodes) {
                string name = child.Name.ToUpper();
                if (name == "NAME") {
                    codexFile.Name = child.InnerText;
                } else if (name == "AUTHOR") {
                    codexFile.Author = child.InnerText;
                } else if (name == "VERSION") {
                    codexFile.Version = child.InnerText;
                } else if (name == "GAMESYSTEM") {
                    codexFile.System = child.InnerText;
                    string path = Application.StartupPath + "/data/Game System/" + codexFile.System + "/";
                    if (!File.Exists(path + "index.xml")) {
                        throw new Exception("Missing game system file: " + path);
                    } else {
                        Console.WriteLine("Parsing Game System: " + codexFile.System);
                        codexFile = this.ParseGameSystem(codexFile, path);
                    }
                } else if (name == "SPECIALRULES") {
                    codexFile.SpecialRules.Clear();
                    foreach (XmlNode srNode in child.ChildNodes) {
                        if (srNode.Name.ToUpper() == "SPECIALRULE") {
                            SpecialRule rule = new SpecialRule();
                            foreach (XmlNode srSubNode in srNode.ChildNodes) {
                                if (srSubNode.Name.ToUpper() == "NAME") {
                                    rule.Name = srSubNode.InnerText;
                                } else if (srSubNode.Name.ToUpper() == "DESCRIPTION") {
                                    rule.Description = srSubNode.InnerText;
                                }
                            }
                            codexFile.SpecialRules.Add(rule);
                        }
                    }
                }
            }
            return codexFile;
        }

        public CodexFile ParseGameSystem(CodexFile codexFile, string path) {
            XmlDocument doc = new XmlDocument();
            GameSystem gs = new GameSystem();
            gs.Name = codexFile.System;
            //gs.Path = Application.StartupPath + "/" + path;
            gs.Path = path;
            doc.Load(gs.Path + "index.xml");
            foreach (XmlNode headerNode in doc.GetElementsByTagName("header")[0].ChildNodes) {
                string name = headerNode.Name.ToUpper();
                if (name == "NAME") {
                    gs.Name = headerNode.InnerText;
                } else if (name == "AUTHOR") {
                    gs.Author = headerNode.InnerText;
                } else if (name == "VERSION") {
                    gs.Version = headerNode.InnerText;         
            }
            }
            foreach (XmlNode conditionNode in doc.GetElementsByTagName("conditions")[0].ChildNodes) {
                if (conditionNode.Name.ToUpper() == "CONDITION") {
                    Condition condition = new Condition();
                    foreach (XmlNode childNode in conditionNode.ChildNodes) {
                        string name = childNode.Name.ToUpper();
                        if (name == "CATEGORYNAME") {
                            condition.Category = childNode.InnerText;
                        } else if (name == "MINIMUM") {
                            condition.Minimum = int.Parse(childNode.InnerText);
                        } else if (name == "MAXIMUM") {
                            condition.Maximum = int.Parse(childNode.InnerText);
                        }
                    }
                    gs.Conditions.Add(condition);
                }
            }
            try {
                if (File.Exists(gs.Path + "/logo.gif")) {
                    gs.Icon = System.Drawing.Image.FromFile(gs.Path + "/logo.gif");
                }
            } catch { 
                gs.Icon = null; 
            }
            codexFile.GameSystem = gs;
            return codexFile;
        }

        private CodexFile ParseSpecialRules(CodexFile codexFile) {
            XmlNodeList specialRuleNodes = codexFile.RawData.GetElementsByTagName("specialRule");
            foreach (XmlNode ruleNode in specialRuleNodes) {
                SpecialRule rule = new SpecialRule();
                foreach (XmlNode child in ruleNode.ChildNodes) {
                    string name = child.Name.ToUpper();
                    if (name == "NAME") {
                        rule.Name = child.InnerText;
                    } else if (name == "DESCRIPTION") {
                        rule.Description = child.InnerText;
                    }
                }
                codexFile.SpecialRules.Add(rule);
            }
            return codexFile;
        }

        private CodexFile ParseTransports(CodexFile codexFile, XmlDocument document) {
            if (document == null) {
                document = codexFile.RawData;
            }
            XmlNodeList transportNodes = document.GetElementsByTagName("transports");
            foreach (XmlNode transportNode in transportNodes) {
                foreach (XmlNode childNode in transportNode.ChildNodes) {
                    string name = childNode.Name.ToUpper();
                    if (name == "INCLUDE") {
                        XmlDocument temp = new XmlDocument();
                        temp.Load(codexFile.Path + "/" + childNode.InnerText);
                        Console.WriteLine("Transport recursion into: " + childNode.InnerText);
                        codexFile = this.ParseTransports(codexFile, temp);
                    } else if (name == "UNITTYPE") {
                        Unit unit = Unit.CreateFromNode(codexFile, childNode);
                        codexFile.Transports.Add(unit);
                        Console.WriteLine("- Added Transport: " + unit.Name);
                    }
                }
            }
            return codexFile;
        }

        private CodexFile ParseArmoury(CodexFile codexFile, XmlDocument document)
        {
            if (document == null)
            {
                document = codexFile.RawData;
            }
            XmlNodeList armouryNodes = document.GetElementsByTagName("armoury");
            foreach (XmlNode armouryNode in armouryNodes)
            {
                foreach (XmlNode childNode in armouryNode.ChildNodes)
                {
                    string name = childNode.Name.ToUpper();
                    if (name == "INCLUDE")
                    {
                        XmlDocument temp = new XmlDocument();
                        temp.Load(codexFile.Path + "/" + childNode.InnerText);
                        Console.WriteLine("Armoury recursion into: " + childNode.InnerText);
                        codexFile = this.ParseArmoury(codexFile, temp);
                    }
                    else if (name == "OPTIONS")
                    {
                        List<ArmouryItem> armoury = Armoury.CreateFromNode(codexFile, childNode);
                        Console.WriteLine("- Added Armoury");
                    }
                }
            }
            return codexFile;
        }

        private CodexFile ParseWargear(CodexFile codexFile, XmlDocument document) {
            if (document == null) {
                document = codexFile.RawData;
            }
            XmlNodeList wargearNodes = document.GetElementsByTagName("wargear");
            foreach (XmlNode wargearNode in wargearNodes) {
                if (wargearNode.HasChildNodes) {
                    foreach (XmlNode childNode in wargearNode.ChildNodes) {
                        string name = childNode.Name.ToUpper();
                        if (name == "INCLUDE") {
                            XmlDocument temp = new XmlDocument();
                            temp.Load(codexFile.Path + "/" + childNode.InnerText);
                            Console.WriteLine("Wargear Recursion into: " + childNode.InnerText);
                            codexFile = this.ParseWargear(codexFile, temp);
                        } else if(name == "ITEM" ) {
                            codexFile = this.ParseWargearItem(codexFile, childNode);
                        } else if (name == "WARGEARGROUP") {
                            codexFile = this.ParseWargearGroup(codexFile, childNode);
                        }
                    }
                }
            }
            return codexFile;
        }

        private CodexFile ParseWargearItem(CodexFile codexFile, XmlNode itemNode) {
            Wargear wargear = new Wargear();
            foreach (XmlNode childNode in itemNode.ChildNodes) {
                string name = childNode.Name.ToUpper();
                string value = childNode.InnerText;
                if (name == "ID") {
                    wargear.ID = int.Parse(value);
                } else if (name == "NAME") {
                    wargear.Name = value;
                } else if (name == "DESCRIPTION") {
                    wargear.Description = value;
                } else if (name == "TYPE") {
                    wargear.Type = value;
                } else if (name == "FLAG")
                {
                    switch (value.ToUpper())
                    {
                        case "CCW":
                        case "CLOSE COMBAT WEAPON": wargear.IsCCW = true; break;
                        case "DCCW":
                        case "DREADNOUGHT CLOSE COMBAT WEAPON": wargear.IsDCCW = true; break;
                        case "TWO HANDED":
                        case "TWO-HANDED": wargear.IsTwoHanded = true; break;
                        case "PF":
                        case "POWERFIST":
                        case "POWER FIST": wargear.IsPowerfist = true; break;
                        case "TH":
                        case "THUNDER HAMMER": wargear.IsThunderHammer = true; break;
                        case "LC":
                        case "LIGHTNING CLAW": wargear.IsLightningClaw = true; break;
                        case "ALWAYSHIDE":
                        case "ALWAYS HIDE": wargear.AlwaysHide = true; break;
                        default: break;
                    }
                } else if (name == "MODIFIERS") {
                    foreach (XmlNode modiferNode in childNode.ChildNodes) {
                        AttributeModifier modifer = new AttributeModifier();
                        foreach (XmlNode modifierChildNode in modiferNode.ChildNodes) {
                            if (modifierChildNode.Name.ToUpper() == "ATTRIBUTE") {
                                modifer.Attribute = modifierChildNode.InnerText;
                            } else if (modifierChildNode.Name.ToUpper() == "VALUE") {
                                modifer.Amount = int.Parse(modifierChildNode.InnerText);
                            }else if (modifierChildNode.Name.ToUpper() == "MULTIPLIER") {
                                modifer.Multiplier = int.Parse(modifierChildNode.InnerText);
                            }
                        }
                        wargear.Modifiers.Add(modifer);
                    }
                }
            }
            if (wargear.Type == null) {
                wargear.Type = "Not specified";
            }
            codexFile.Wargear.Add(wargear);
            return codexFile;
        }

        private CodexFile ParseWargearGroup(CodexFile codexFile, XmlNode groupNode) {
            WargearGroup group = new WargearGroup();
            foreach (XmlNode childNode in groupNode.ChildNodes) {
                string name = childNode.Name.ToUpper();
                string value = childNode.InnerText;
                if (name == "ID") {
                    group.ID = int.Parse(value);
                } else if (name == "NAME") {
                    group.Name = value;
                } else if (name == "CONTAINSNAMED") {
                    group.Raw = value;
                }
            }
            codexFile.WargearGroups.Add(group);
            return codexFile;
        }

        private CodexFile DelayedParse(CodexFile codexFile) {
            foreach (WargearGroup group in codexFile.WargearGroups) {
                if (!group.Parsed) {
                    string[] wargearItems = group.Raw.Split(new char[] { ',' });
                    foreach (string name in wargearItems) {
                        foreach (Wargear wargear in codexFile.Wargear) {
                            if (name.ToUpper() == wargear.Name.ToUpper()) {
                                group.Wargear.Add(wargear);
                                break;
                            }
                        }
                    }
                    group.Parsed = true;
                }
            }
            return codexFile;
        }

        private CodexFile ParseBaseUnits(CodexFile codexFile) {
            XmlNodeList nodes = codexFile.RawData.GetElementsByTagName("baseUnit");
            foreach (XmlNode node in nodes) {
                BaseUnit unit = BaseUnit.CreateFromNode(node);
                codexFile.BaseUnits.Add(unit);
            }
            return codexFile;
        }

        private CodexFile ParseUnitEntries(CodexFile codexFile, XmlDocument doc) {
            if( doc == null ) {
                doc = codexFile.RawData;
            }
            XmlNodeList nodes = doc.GetElementsByTagName("units");
            foreach (XmlNode node in nodes) {
                foreach (XmlNode childNode in node.ChildNodes) {
                    string name = childNode.Name.ToUpper();
                    if (name == "INCLUDE") {
                        XmlDocument temp = new XmlDocument();
                        temp.Load(codexFile.Path + "/" + childNode.InnerText);
                        Console.WriteLine("Unit Recursion into: " + childNode.InnerText);
                        codexFile = this.ParseUnitEntries(codexFile, temp);
                    } else if (name == "UNIT") {
                        CodexEntry entry = CodexEntry.CreateFromNode(codexFile,childNode);
                        Console.WriteLine("Added Codex Entry: " + entry.Name);
                        codexFile.CodexEntries.Add(entry);
                    }
                }
            }
            return codexFile;
        }
        #endregion
    }
}
